package kycadminrepo

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"html/template"
	"strings"

	"code.bydev.io/cht/customer/kyc-stub.git/pkg/bybit/kyc/common/enums"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/helper"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycadminapi/dto"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycadminrepo/o_kyc_action_state"
	"git.bybit.com/svc/go/pkg/bdal"
	"git.bybit.com/svc/go/pkg/bdal/bqb"
	"git.bybit.com/svc/go/pkg/bzap"
	"git.bybit.com/svc/stub/pkg/pb/enums/ekycadmin"
	"git.bybit.com/svc/stub/pkg/pb/enums/kyc"
	_ "go.elastic.co/apm/module/apmsql/mysql"
	"go.uber.org/zap"
)

func (r *repo) GetActionList(ctx context.Context, data *dto.ActionListCondition) ([]*o_kyc_action_state.Record, int64, error) {
	var records []*o_kyc_action_state.Record
	// 拼接where条件
	var fns []bqb.Condition
	var argsMapm = make(bqb.Args)

	r.logger.Info("GetActionList", zap.Any("condition", data))

	pool, err := r.kycDB.DBDefaultReplicaNodePool(ctx)
	if err != nil {
		bzap.LogError(r.logger.With(zap.Any("ActionListCondition", data)), "GetActionList db poll error", err)
		return nil, 0, err
	}

	actTag := false
	if data.MemberID > 0 {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldMemberId.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldMemberId.Param()] = data.MemberID
	}
	if data.RecordID != "" {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldRecordId.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldRecordId.Param()] = template.HTMLEscapeString(data.RecordID)
	}
	if data.Country != "" {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldCountry.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldCountry.Param()] = template.HTMLEscapeString(data.Country)
	}
	if data.Provider != kyc.KYCProvider_PROVIDER_DEFAULT {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldChannel.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldChannel.Param()] = int8(data.Provider)
	}
	if data.Decision != ekycadmin.KYCAdminDecision_DECISION_DEFAULT {
		switch data.Decision {
		case ekycadmin.KYCAdminDecision_DECISION_PENDING:
			actTag = true
		case ekycadmin.KYCAdminDecision_DECISION_PASS:
			fns = append(fns, func(wb *bqb.WhereBuilder) {
				o_kyc_action_state.FldDecision.EQ(wb)
			})
			argsMapm[o_kyc_action_state.FldDecision.Param()] = int8(kyc.MemberKYCResult_RESULT_PASS)
		case ekycadmin.KYCAdminDecision_DECISION_REJECT:
			fns = append(fns, func(wb *bqb.WhereBuilder) {
				o_kyc_action_state.FldDecision.EQ(wb)
			})
			argsMapm[o_kyc_action_state.FldDecision.Param()] = int8(kyc.MemberKYCResult_RESULT_REJECT)
		}
	}
	if data.Level >= 0 {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldLevel.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldLevel.Param()] = int8(data.Level)
	}
	if data.DocNumber != "" {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldDocNumber.EQ(wb)
		})
		// DB 存储的是密文，查询到时候将原文加密后查询
		dn, errSec := helper.EnAES256CodeString(r.secHub, data.DocNumber)
		if errSec != nil {
			bzap.LogError(
				r.logger.With(
					zap.Any("ActionListCondition", data),
					zap.Any("reqID", helper.GetReqID(ctx))),
				"GetActionList sechub encrypt DocNumber error", errSec)
		}
		argsMapm[o_kyc_action_state.FldDocNumber.Param()] = dn
	}

	if actTag {
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldAction.EQ(wb)
		})
		argsMapm[o_kyc_action_state.FldAction.Param()] = int8(ekycadmin.KYCAdminAction_ACTION_PENDING)
	} else {
		// 过滤 ticket 和 init
		fns = append(fns, func(wb *bqb.WhereBuilder) {
			o_kyc_action_state.FldAction.In(wb)
		})
		argsMapm[o_kyc_action_state.FldAction.Param()] = bqb.InValuesInt32{
			int32(ekycadmin.KYCAdminAction_ACTION_CREATE),
			int32(ekycadmin.KYCAdminAction_ACTION_PENDING),
			int32(ekycadmin.KYCAdminAction_ACTION_COMPLETE),
			int32(ekycadmin.KYCAdminAction_ACTION_RESET),
		}
	}

	sqlCnt := o_kyc_action_state.Count().WhereC(fns...).LimitC(1).SQL()
	total, errCnt := bdal.ScanInt(sqlCnt.QueryArgs(ctx, pool, argsMapm))
	if errCnt != nil {
		bzap.LogError(r.logger.With(
			zap.Any("reqID", helper.GetReqID(ctx)),
			zap.Any("ActionListCondition", data),
		),
			"GetActionList Count error", errCnt)

		return nil, 0, errCnt
	}
	if total == 0 {
		r.logger.Info("GetActionList Count zero", zap.Any("condition", data))
		return nil, 0, nil
	}

	oft := data.PageSize * (data.PageNo - 1)
	slt := o_kyc_action_state.Select()
	slt.Limit(int(data.PageSize))
	slt.Offset(int(oft))
	sqlSlt := slt.WhereC(fns...).OrderByC(o_kyc_action_state.FldProviderCreateAt, bqb.DESC).LimitC(int(data.PageSize)).SQL()

	records, err = o_kyc_action_state.ScanRecords(sqlSlt.QueryArgs(ctx, pool, argsMapm))
	if err != nil {
		bzap.LogError(r.logger.With(
			zap.Any("reqID", helper.GetReqID(ctx)),
			zap.Any("ActionListCondition", data),
		),
			"GetActionList ScanRecords error", err)

		return nil, 0, err
	}

	dcsMps, err := r.GetAllDecisionMaps(ctx)
	if err != nil {
		bzap.LogError(r.logger.With(
			zap.Any("reqID", helper.GetReqID(ctx)),
			zap.Any("ActionListCondition", data),
		),
			"GetActionList ScanRecords error", err)
	} else {
		for _, record := range records {
			if record.Decision == 2 {
				rsn := strings.Split(record.RejectReason, ",")
				find := false
				if len(rsn) > 0 {
					for _, s := range rsn {
						k := fmt.Sprintf("%d-%s", record.Channel, s)
						if cmt, ok := dcsMps[k]; ok {
							record.RejectReason = cmt
							find = true
							break
						}
					}
				}
				if !find {
					record.RejectReason = config.CustomerReasonDefault
				}
			}
		}
	}

	return records, int64(total), nil
}

func (r *repo) GetActionDetail(ctx context.Context, data *dto.ActionDetailCondition) (*o_kyc_action_state.Record, error) {
	var record *o_kyc_action_state.Record

	pool, err := r.kycDB.DBDefaultReplicaNodePool(ctx)
	if err != nil {
		return nil, err
	}

	record, err = o_kyc_action_state.Find(ctx, pool, data.ID)
	switch {
	case err == nil:
		return record, nil
	case errors.Is(err, sql.ErrNoRows):
		return nil, nil
	default:
		return nil, err
	}
}

func (r *repo) GetActionByIdempotentID(ctx context.Context, idempotentID string) (*o_kyc_action_state.Record, error) {
	var record *o_kyc_action_state.Record

	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	idpSQL := o_kyc_action_state.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_state.FldIdempotentId.EQ(wb)
	}).LimitC(1).SQL()

	record, err = o_kyc_action_state.ScanRecordNil(idpSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_state.FldIdempotentId.Param(): idempotentID,
	}))
	if err != nil {
		return nil, err
	}

	return record, nil
}

func (r *repo) GetPoaRejectTimes(ctx context.Context, memberID int64, channel int8) (int, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return 0, err
	}
	var argsMapm = make(bqb.Args)

	argsMapm[o_kyc_action_state.FldChannel.Param()] = channel
	argsMapm[o_kyc_action_state.FldLevel.Param()] = 2
	argsMapm[o_kyc_action_state.FldMemberId.Param()] = memberID
	argsMapm[o_kyc_action_state.FldDecision.Param()] = 2

	sqlCnt := o_kyc_action_state.Count().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_state.FldChannel.EQ(wb)
		o_kyc_action_state.FldLevel.EQ(wb)
		o_kyc_action_state.FldMemberId.EQ(wb)
		o_kyc_action_state.FldDecision.EQ(wb)
	}).LimitC(1).SQL()

	total, errCnt := bdal.ScanInt(sqlCnt.QueryArgs(ctx, pool, argsMapm))
	if errCnt != nil {
		r.logger.Error("CountCountryConfigFailed", zap.Error(err), zap.Reflect("memberID", memberID))
		return 0, errCnt
	}
	return total, nil
}

// GetOnfidoApprovedPOIRecords 获取Onfido已经通过的POI记录
func (r *repo) GetOnfidoApprovedPOIRecords(ctx context.Context, startID int64, limit int) ([]*o_kyc_action_state.Record, error) {
	var records []*o_kyc_action_state.Record
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return records, err
	}

	selectSQL := o_kyc_action_state.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_state.FldChannel.EQ(wb)
		o_kyc_action_state.FldLevel.EQ(wb)
		o_kyc_action_state.FldDecision.EQ(wb)
		wb.GT(o_kyc_action_state.FldId, o_kyc_action_state.FldId.ParamPlaceholder())
	}).OrderByC(o_kyc_action_state.FldId, bqb.ASC).LimitC(limit).SQL()

	records, err = o_kyc_action_state.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_state.FldChannel.Param():  int8(kyc.KYCProvider_PROVIDER_ONFIDO),
		o_kyc_action_state.FldLevel.Param():    int8(enums.MemberKYCLevel_LEVEL_1),
		o_kyc_action_state.FldDecision.Param(): int8(kyc.MemberKYCResult_RESULT_PASS),
		o_kyc_action_state.FldId.Param():       startID,
	}))

	if err != nil {
		return records, err
	}
	return records, nil
}
